﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Zhaoxi.ThreadShow.Common;

namespace Zhaoxi.ThreadShow
{
    public partial class frmSSQ : Form
    {
        /// 多线程双色球项目--
        /// 1 理解需求:
        /// 双色球：投注号码由6个红色球号码和1个蓝色球号码组成。
        /// 红色球号码从01--33中选择,不重复
        /// 蓝色球号码从01--16中选择  

        public frmSSQ() //主线程执行
        {
            InitializeComponent(); //主线程执行
            //this.btnStop.Enabled = false;
        }


        #region Data 
        /// <summary>
        /// 红球集合  其实可以写入配置文件
        /// </summary>
        private string[] RedNums =
        {
            "01","02","03","04","05","06","07","08","09","10",
            "11","12","13","14","15","16","17","18","19","20",
            "21","22","23","24","25","26","27","28","29","30",
            "31","32","33"
        };

        /// <summary>
        /// 蓝球集合---也可以写在配置文件中；
        /// </summary>
        private string[] BlueNums =
        {
            "01","02","03","04","05","06","07","08","09","10",
            "11","12","13","14","15","16"
        };

        //Index 0--15

        #endregion

        /// <summary>
        /// 标准锁---锁对象
        /// </summary>
        private static object Lock_Object = new object();


        private List<Task> TasksList;
        CancellationTokenSource cts;

        /// <summary>
        /// 点击开始
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStart_Click(object sender, EventArgs e)
        {
            #region 初始化动作
            //就是把7个球号码的数据全部置为00
            this.lblBlue.Text = "00";
            this.lblRed1.Text = "00";
            this.lblRed2.Text = "00";
            this.lblRed3.Text = "00";
            this.lblRed4.Text = "00";
            this.lblRed5.Text = "00";
            this.lblRed6.Text = "00";
            #endregion


            this.btnStart.Enabled = false;
            this.btnStart.Text = "开奖中~~";
            this.btnStop.Enabled = true;
            cts = new CancellationTokenSource();
            TasksList = new List<Task>();




            //诉求: 每个球的变化要相互独立---多线程
            //开启7个线程, 每个线程对应一个秋号码

            //连续的变化~  
            //1. 取值---每个球的号码数字
            //2. 赋值给每个秋号码数字
            //3. 球好不断地变化---循环~~

            //Task.Run(() => { });
            //Task.Run(() => { });
            //Task.Run(() => { });
            //Task.Run(() => { });
            //Task.Run(() => { });
            //Task.Run(() => { });
            //Task.Run(() => { });

            foreach (var control in this.gboSSQ.Controls)
            {
                if (control is Label)
                {

                    //分情况讨论  红色球和蓝色球~~ 
                    Label label = (Label)control;
                    if (label.Name.Contains("Blue")) //蓝色球
                    {

                        TasksList.Add(Task.Run(() =>
                        {
                            while (!cts.IsCancellationRequested)
                            {
                                //连续的变化~  
                                //1. 取值---每个球的号码数字
                                //确定索引
                                //new Random().Next();

                                int index = RandomHelper.GetRandomNumberDelay(0, 16);
                                string blueNumb = BlueNums[index];

                                //2. 赋值给每个秋号码数字
                                //label.Text = blueNumb;  //报错  跨 线程去操作了控件~~ 不允许, 控件的初始化 是由主线程来完成的, 要赋值也需要让主线程来完成~

                                //子线程委托主线程帮助完成一个赋值~~
                                this.Invoke(new Action(() =>
                                {
                                    label.Text = blueNumb;
                                }));

                                //3. 球号码断地变化---循环~~
                            }
                        }));

                    }
                    else  //红色球
                    {

                        ///可能出现重复---和需求不符合~~
                        ///保证不能重复~  如何保证?
                        ///1. 直接加锁吗?  反多线程
                        ///2. 已出现的数字从数组移除？
                        ///3. 每次赋值的时候, 判断下界面上是否已经存在这个值~~ 
                        TasksList.Add(Task.Run(() =>
                        {
                            while (!cts.IsCancellationRequested)
                            {
                                int index = RandomHelper.GetRandomNumberDelay(0, 33);
                                string redNumb = RedNums[index];

                                lock (Lock_Object)
                                {
                                    //有概率重复~
                                    //证明 这个currentNumlist 集合中有重复数据,就一定是发生过了重复~
                                    List<string> currentNumlist = GetCurrentNumList();
                                    //还是重复~~ 怎么办? 
                                    //如果这要赋的值 在界面上已经存在了, 不赋值, 重新计算,直到界面上不存在这个值,去赋值~~
                                    //在在这里就不能并发了---lock

                                    if (!currentNumlist.Contains(redNumb))
                                    {
                                        this.Invoke(new Action(() =>
                                        {
                                            label.Text = redNumb;
                                        }));
                                    }
                                    else
                                    {
                                        //继续循环  重新计算~~
                                    }

                                }

                            }
                        })); 
                    }
                }

            }

            Task.Run(() =>
            {
                Task.WaitAll(TasksList.ToArray());
                this.Invoke(() =>
                {
                    this.btnStart.Enabled = true;
                    this.btnStart.Text = "Start";
                    this.btnStop.Enabled = false; 

                });
                ShowResult();
            });

        }

        /// <summary>
        /// 获取界面上已经存在红色球号码
        /// </summary>
        /// <returns></returns>
        private List<string> GetCurrentNumList()
        {
            List<string> currentNumList = new List<string>();

            foreach (var control in this.gboSSQ.Controls)
            {
                if (control is Label && ((Label)control).Name.Contains("Red"))
                {
                    currentNumList.Add(((Label)control).Text);
                }
            }

            //currentNumList.Count(c=>c=="00")==0  保证当前集合中,没有任何一个数据为00
            //currentNumList.Distinct().Count()< currentNumList.Count():  结合去掉重复后的元素数据量小于 去重复之前的数量
            if (currentNumList.Count(c => c == "00") == 0 && currentNumList.Distinct().Count() < currentNumList.Count())
            {
                Console.WriteLine("============================");
                foreach (var item in currentNumList)
                {
                    Console.WriteLine(item);
                }
            }


            return currentNumList;
        }



        /// <summary>
        /// 点击结束 ---必然是主线程来执行的
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStop_Click(object sender, EventArgs e)
        {

            //1. 停止球号码的变化 
            cts.Cancel();

            ////2. 弹框弹出 开出的球号码的值
            ////应该等待所有的球号码停止变化了之后, 再弹出结果

            //Task.Run(() =>
            //{
            //    Task.WaitAll(TasksList.ToArray());//等待球号码全部停止变化~
            //    ShowResult();

            //    this.Invoke(() =>
            //    {
            //        this.btnStart.Enabled = true;
            //        this.btnStart.Text = "Start";
            //        this.btnStop.Enabled = false;
            //    });  
            //});

            //弹出结果 
            //问题:界面结果和弹出结果不一致~ 
            //会不会有什么问题?  停止的时候, 界面直接卡死~~   多线程使用的时候, 死锁问题~~
            //死锁: 什么死锁?  相互等待了, 谁也不让谁, 就一直等待了~~



        }


        /// <summary>
        /// 弹框提示数据
        /// </summary>
        private void ShowResult()
        {
            MessageBox.Show(string.Format("本期双色球结果为：{0} {1} {2} {3} {4} {5}  蓝球{6}"
               , this.lblRed1.Text
               , this.lblRed2.Text
               , this.lblRed3.Text
               , this.lblRed4.Text
               , this.lblRed5.Text
               , this.lblRed6.Text
               , this.lblBlue.Text));
        }

    }
}
